package com.wf.captcha.utils;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 解决自定义字体读取时，产生.tmp临时文件耗磁盘的问题。
 *
 * 解决思路:
 * Font类的createFont有个重载方法–>java.awt.Font#createFont(int, java.io.File),
 * 不产生临时文件获取字体代码实现
 * <code>
 *  URL url = FontLoader.class.getResource("font/SourceHanSansCN-Regular.otf");
 *  String pathString = url.getFile();
 *  Font selfFont = Font.createFont(Font.TRUETYPE_FONT, new File(pathString));
 * </code>
 * 上面的解决方案会导致另一个问题，字体文件在生产环境是在jar包里，部分操作系统环境下，直接读取读取不到，只能通过流的方式获取。
 *
 * 因此，本方案采用的办法是把jar包中的字体文件复制到java.io.tmpdir临时文件夹中
 * ，再采用<code>java.awt.Font#createFont(int, java.io.File)</code>的方式产生字体，既解决了临时文件tmp消耗磁盘的问题，也解决了
 * 部分操作系统下读不到文件的问题。
 *
 * @author zrh 455741807@qq.com
 * @date 2022-05-07
 *
 */
public class FontsUtil {
    private static final LoadingCache<CacheKey, Font> CACHE = CacheBuilder.newBuilder()
            .expireAfterAccess(15, TimeUnit.MINUTES)
            .build(new CacheLoader<CacheKey, Font>(){
                @Override
                public Font load(CacheKey key) {
                    return doGetFontFromCache(key);
                }
            });

    private static Font doGetFontFromCache(CacheKey key) {
        if(key == null){
            return null;
        }
        String fontName = key.getFontName();
        int style = key.getStyle();
        float size = 0.0f;
        try {
            size = Float.parseFloat(key.getSize());
        }catch (Exception e){
            e.printStackTrace();
        }
        return getFont(fontName, style, size);
    }

    /**
     * 手动复制字体文件到临时目录. 调用传文件的构造方法创建字体，相同的字体使用缓存。
     * @param fontName 字体文件名称
     * @param style
     * @param size
     * @return
     */
    public static Font getFontFromCache(String fontName, int style, float size) {
        Font font = null;
        try {
            font = CACHE.get(new CacheKey(fontName, style, String.valueOf(size)));
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return font;
    }

    /**
     * 手动复制字体文件到临时目录. 调用传文件的构造方法创建字体
     * @param fontName 字体文件名称
     * @param style
     * @param size
     * @return
     */
    public static Font getFont(String fontName, int style, float size) {
        Font font = null;

        File tempFontFile = new File(System.getProperty("java.io.tmpdir") + File.separator + fontName);
        if(!tempFontFile.exists()){
            //临时文件不存在
            copyTempFontFile(fontName, tempFontFile);
        }
        if(tempFontFile.exists()) {
            try {
                font = Font.createFont(Font.TRUETYPE_FONT, tempFontFile).deriveFont(style, size);;
            } catch (FontFormatException | IOException e) {
                e.printStackTrace();
                tempFontFile.delete();
            }
        }
        return font;
    }

    /**
     * 复制字体文件到临时文件目录
     * @param fontName
     * @param tempFontFile
     */
    private static synchronized void copyTempFontFile(String fontName, File tempFontFile){
        try(InputStream is = FontsUtil.class.getClassLoader().getResourceAsStream(fontName)){
            FileUtil.copyToFile(is, tempFontFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static class CacheKey {
        private String fontName;
        private int style;
        private String size;

        public CacheKey(String fontName, int style, String size) {
            this.fontName = fontName;
            this.style = style;
            this.size = size;
        }

        public String getFontName() {
            return fontName;
        }

        public int getStyle() {
            return style;
        }

        public String getSize() {
            return size;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            CacheKey cacheKey = (CacheKey) o;
            return style == cacheKey.style &&
                    Objects.equals(fontName, cacheKey.fontName) &&
                    Objects.equals(size, cacheKey.size);
        }

        @Override
        public int hashCode() {
            return Objects.hash(fontName, style, size);
        }
    }
}
